#include <sys/select.h>
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Log.hpp"

#define IP "101.34.209.208"
#define DEFAULT_PORT 8080

const int array_size = sizeof(fd_set) * 8;

/*select的优缺点：
    优点: select只负责等待，可以等待多个fd。相对于多进程与多线程，IO效率更高，不使用多进程多线程也能同时与多个客户端交互。
    缺点: 
    1. 每次需要重置参数fd_set
    2. 需要第三方数组的参与，会出现大量遍历操作
    3. OS需要不断从用户态切换到内核态，在两态之间进行拷贝工作
    4. 需要OS遍历所有关心的文件描述符，来判断是否就绪，也是max_fd的作用
    5. fd_set是系统提供的位图类型，大小是固定的。能管理的fd有上限。
*/

class SelectServer
{
public:
    SelectServer(int port = DEFAULT_PORT) : _sock(new TcpSocket(port))
    {
        _sock->BulidListenSocket(port);
        for(int i = 0; i < array_size; i++)
        {
            _rfds_array[i] = nullptr;
        }
        _rfds_array[0] = _sock;
    }

    void Handle(fd_set &rfds)
    {
        // 监听套接字读事件就绪
        if(FD_ISSET(_rfds_array[0]->GetSocket(), &rfds))
        {
            std::string peerip;
            int peerport;
            Socket *newsock = _sock->AcceptSocket(&peerip, &peerport);
            ILOG("获取新连接, IP:%s, port:%d", peerip.c_str(), peerport);
            int pos = 0;
            for(; pos < array_size; pos++)
            {
                if(_rfds_array[pos] == nullptr)
                {
                    _rfds_array[pos] = newsock;
                    break;
                }
            }
            if(pos == array_size)
            {
                newsock->Close();
                delete newsock;
                DLOG("sever is full ...");
            }
        }
        for(int i = 1; i < array_size; i++)
        {
            // 普通套接字读事件就绪
            if(_rfds_array[i] != nullptr && FD_ISSET(_rfds_array[i]->GetSocket(), &rfds))
            {
                char buffer[1024];
                bool ret = _rfds_array[i]->Recv(buffer, 1024);
                if(ret)
                {
                    ILOG("client# %s", buffer);
                    std::string str = "你好1111111222";
                    _rfds_array[i]->Send(str);
                }
                else
                {
                    ELOG("client close or recv error ...");
                    _rfds_array[i]->Close();
                    delete _rfds_array[i];
                    _rfds_array[i] = nullptr;
                }
            }
        }
        
    }

    void Loop()
    {
        fd_set rfds;
        while(1)
        {
            FD_ZERO(&rfds);
            int max_fd = _sock->GetSocket();
            for(int i = 0; i < array_size; i++)
            {
                if(_rfds_array[i] != nullptr)
                {
                    int fd = _rfds_array[i]->GetSocket();
                    FD_SET(fd, &rfds);
                    if(fd > max_fd) max_fd = fd;
                }
            }
            
            struct timeval tv = {5, 0};
            int ret = select(max_fd + 1, &rfds, nullptr, nullptr, &tv);
            if(ret < 0)
            {
                ELOG("select error ...");
                exit(1);
            }
            else if(ret > 0)
            {
                DLOG("剩余时间: %d:%d", tv.tv_sec, tv.tv_usec);
            }
            else
            {
                // DLOG("timeout ...");
            }
            Handle(rfds);
        }
    }


private:
    Socket* _sock;
    Socket* _rfds_array[array_size];
};